package references

import (
	"jvm/instructions/base"
	"jvm/rtda"
	"jvm/rtda/heap"
)

type INVOKE_INTERFACE struct {
	index uint
	//count	uint8
	//zero  uint8
}

func (ii *INVOKE_INTERFACE) FetchOperands(reader *base.BytecodeReader) {
	ii.index = uint(reader.ReadUint16())
	reader.ReadUint8() // count
	reader.ReadUint8() // zero
}

func (ii *INVOKE_INTERFACE) Execute(jFrame *rtda.JFrame) {
	cp := jFrame.Method().Class().ConstantPool()
	methodRef := cp.GetConstant(ii.index).(*heap.InterfaceMethodRef)
	resolvedMethod := methodRef.ResolvedInterfaceMethod()
	if resolvedMethod.IsStatic() || resolvedMethod.IsPrivate() {
		panic("java.lang.IncompatibleClassChangeError")
	}
	ref := jFrame.OperandStack().GetRefFromTop(resolvedMethod.ArgSlotCount() - 1)
	if ref == nil {
		panic("java.lang.NullPointerException")
	}
	if !ref.Class().IsImplements(methodRef.ResolvedClass()) {
		panic("java.lang.IncompatibleClassChangeError")
	}
	methodToBeInvoked := heap.LookupMethodInClass(ref.Class(), methodRef.Name(), methodRef.Descriptor())

	if methodToBeInvoked == nil || methodToBeInvoked.IsAbstract() {
		panic("java.lang.AbstractMethodError")
	}
	if !methodToBeInvoked.IsPublic() {
		panic("java.lang.IllegalAccessError")
	}
	base.InvokeMethod(jFrame, methodToBeInvoked)
}
